using System;
using System.Runtime.InteropServices;

namespace Leap
{
	public class Matrix : IDisposable
	{
		private HandleRef swigCPtr;

		protected bool swigCMemOwn;

		public Vector xBasis
		{
			get
			{
				IntPtr intPtr = LeapPINVOKE.Matrix_xBasis_get(swigCPtr);
				return (intPtr == IntPtr.Zero) ? null : new Vector(intPtr, false);
			}
			set
			{
				LeapPINVOKE.Matrix_xBasis_set(swigCPtr, Vector.getCPtr(value));
			}
		}

		public Vector yBasis
		{
			get
			{
				IntPtr intPtr = LeapPINVOKE.Matrix_yBasis_get(swigCPtr);
				return (intPtr == IntPtr.Zero) ? null : new Vector(intPtr, false);
			}
			set
			{
				LeapPINVOKE.Matrix_yBasis_set(swigCPtr, Vector.getCPtr(value));
			}
		}

		public Vector zBasis
		{
			get
			{
				IntPtr intPtr = LeapPINVOKE.Matrix_zBasis_get(swigCPtr);
				return (intPtr == IntPtr.Zero) ? null : new Vector(intPtr, false);
			}
			set
			{
				LeapPINVOKE.Matrix_zBasis_set(swigCPtr, Vector.getCPtr(value));
			}
		}

		public Vector origin
		{
			get
			{
				IntPtr intPtr = LeapPINVOKE.Matrix_origin_get(swigCPtr);
				return (intPtr == IntPtr.Zero) ? null : new Vector(intPtr, false);
			}
			set
			{
				LeapPINVOKE.Matrix_origin_set(swigCPtr, Vector.getCPtr(value));
			}
		}

		public static Matrix Identity => new Matrix(LeapPINVOKE.Matrix_Identity_get(), false);

		public Matrix(IntPtr cPtr, bool cMemoryOwn)
		{
			swigCMemOwn = cMemoryOwn;
			swigCPtr = new HandleRef(this, cPtr);
		}

		public static HandleRef getCPtr(Matrix obj)
		{
			return obj?.swigCPtr ?? new HandleRef(null, IntPtr.Zero);
		}

		~Matrix()
		{
			Dispose();
		}

		public virtual void Dispose()
		{
			lock (this)
			{
				if (swigCPtr.Handle != IntPtr.Zero)
				{
					if (swigCMemOwn)
					{
						swigCMemOwn = false;
						LeapPINVOKE.delete_Matrix(swigCPtr);
					}
					swigCPtr = new HandleRef(null, IntPtr.Zero);
				}
				GC.SuppressFinalize(this);
			}
		}

		public static Matrix operator *(Matrix m1, Matrix m2)
		{
			return m1._operator_mul(m2);
		}

		public float[] ToArray3x3(float[] output)
		{
			output[0] = xBasis.x;
			output[1] = xBasis.y;
			output[2] = xBasis.z;
			output[3] = yBasis.x;
			output[4] = yBasis.y;
			output[5] = yBasis.z;
			output[6] = zBasis.x;
			output[7] = zBasis.y;
			output[8] = zBasis.z;
			return output;
		}

		public double[] ToArray3x3(double[] output)
		{
			output[0] = (double)xBasis.x;
			output[1] = (double)xBasis.y;
			output[2] = (double)xBasis.z;
			output[3] = (double)yBasis.x;
			output[4] = (double)yBasis.y;
			output[5] = (double)yBasis.z;
			output[6] = (double)zBasis.x;
			output[7] = (double)zBasis.y;
			output[8] = (double)zBasis.z;
			return output;
		}

		public float[] ToArray3x3()
		{
			return ToArray3x3(new float[9]);
		}

		public float[] ToArray4x4(float[] output)
		{
			output[0] = xBasis.x;
			output[1] = xBasis.y;
			output[2] = xBasis.z;
			output[3] = 0f;
			output[4] = yBasis.x;
			output[5] = yBasis.y;
			output[6] = yBasis.z;
			output[7] = 0f;
			output[8] = zBasis.x;
			output[9] = zBasis.y;
			output[10] = zBasis.z;
			output[11] = 0f;
			output[12] = origin.x;
			output[13] = origin.y;
			output[14] = origin.z;
			output[15] = 1f;
			return output;
		}

		public double[] ToArray4x4(double[] output)
		{
			output[0] = (double)xBasis.x;
			output[1] = (double)xBasis.y;
			output[2] = (double)xBasis.z;
			output[3] = 0.0;
			output[4] = (double)yBasis.x;
			output[5] = (double)yBasis.y;
			output[6] = (double)yBasis.z;
			output[7] = 0.0;
			output[8] = (double)zBasis.x;
			output[9] = (double)zBasis.y;
			output[10] = (double)zBasis.z;
			output[11] = 0.0;
			output[12] = (double)origin.x;
			output[13] = (double)origin.y;
			output[14] = (double)origin.z;
			output[15] = 1.0;
			return output;
		}

		public float[] ToArray4x4()
		{
			return ToArray4x4(new float[16]);
		}

		public Matrix()
			: this(LeapPINVOKE.new_Matrix__SWIG_0(), true)
		{
		}

		public Matrix(Matrix other)
			: this(LeapPINVOKE.new_Matrix__SWIG_1(getCPtr(other)), true)
		{
			if (!LeapPINVOKE.SWIGPendingException.Pending)
			{
				return;
			}
			throw LeapPINVOKE.SWIGPendingException.Retrieve();
		}

		public Matrix(Vector _xBasis, Vector _yBasis, Vector _zBasis)
			: this(LeapPINVOKE.new_Matrix__SWIG_2(Vector.getCPtr(_xBasis), Vector.getCPtr(_yBasis), Vector.getCPtr(_zBasis)), true)
		{
			if (!LeapPINVOKE.SWIGPendingException.Pending)
			{
				return;
			}
			throw LeapPINVOKE.SWIGPendingException.Retrieve();
		}

		public Matrix(Vector _xBasis, Vector _yBasis, Vector _zBasis, Vector _origin)
			: this(LeapPINVOKE.new_Matrix__SWIG_3(Vector.getCPtr(_xBasis), Vector.getCPtr(_yBasis), Vector.getCPtr(_zBasis), Vector.getCPtr(_origin)), true)
		{
			if (!LeapPINVOKE.SWIGPendingException.Pending)
			{
				return;
			}
			throw LeapPINVOKE.SWIGPendingException.Retrieve();
		}

		public Matrix(Vector axis, float angleRadians)
			: this(LeapPINVOKE.new_Matrix__SWIG_4(Vector.getCPtr(axis), angleRadians), true)
		{
			if (!LeapPINVOKE.SWIGPendingException.Pending)
			{
				return;
			}
			throw LeapPINVOKE.SWIGPendingException.Retrieve();
		}

		public Matrix(Vector axis, float angleRadians, Vector translation)
			: this(LeapPINVOKE.new_Matrix__SWIG_5(Vector.getCPtr(axis), angleRadians, Vector.getCPtr(translation)), true)
		{
			if (!LeapPINVOKE.SWIGPendingException.Pending)
			{
				return;
			}
			throw LeapPINVOKE.SWIGPendingException.Retrieve();
		}

		public void SetRotation(Vector axis, float angleRadians)
		{
			LeapPINVOKE.Matrix_SetRotation(swigCPtr, Vector.getCPtr(axis), angleRadians);
			if (!LeapPINVOKE.SWIGPendingException.Pending)
			{
				return;
			}
			throw LeapPINVOKE.SWIGPendingException.Retrieve();
		}

		public Vector TransformPoint(Vector arg0)
		{
			Vector result = new Vector(LeapPINVOKE.Matrix_TransformPoint(swigCPtr, Vector.getCPtr(arg0)), true);
			if (LeapPINVOKE.SWIGPendingException.Pending)
			{
				throw LeapPINVOKE.SWIGPendingException.Retrieve();
			}
			return result;
		}

		public Vector TransformDirection(Vector arg0)
		{
			Vector result = new Vector(LeapPINVOKE.Matrix_TransformDirection(swigCPtr, Vector.getCPtr(arg0)), true);
			if (LeapPINVOKE.SWIGPendingException.Pending)
			{
				throw LeapPINVOKE.SWIGPendingException.Retrieve();
			}
			return result;
		}

		public Matrix RigidInverse()
		{
			return new Matrix(LeapPINVOKE.Matrix_RigidInverse(swigCPtr), true);
		}

		private Matrix _operator_mul(Matrix other)
		{
			Matrix result = new Matrix(LeapPINVOKE.Matrix__operator_mul(swigCPtr, getCPtr(other)), true);
			if (LeapPINVOKE.SWIGPendingException.Pending)
			{
				throw LeapPINVOKE.SWIGPendingException.Retrieve();
			}
			return result;
		}

		public bool Equals(Matrix other)
		{
			bool result = LeapPINVOKE.Matrix_Equals(swigCPtr, getCPtr(other));
			if (LeapPINVOKE.SWIGPendingException.Pending)
			{
				throw LeapPINVOKE.SWIGPendingException.Retrieve();
			}
			return result;
		}

		public override string ToString()
		{
			return LeapPINVOKE.Matrix_ToString(swigCPtr);
		}
	}
}
